package asf.core
{
	import asf.events.ApplicationLoadProgressEvent;
	import asf.forms.Forms;
	import asf.loaders.AppLoader;
	
	import br.com.stimuli.loading.BulkLoader;
	import br.com.stimuli.loading.BulkProgressEvent;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.text.StyleSheet;
	import flash.utils.getQualifiedClassName;
	
	[Event( type="asf.events.ApplicationLoadProgressEvent", name="dependenciesLoadStart" )]
	[Event( type="asf.events.ApplicationLoadProgressEvent", name="progress" )]
	[Event( type="asf.events.ApplicationLoadProgressEvent", name="dependenciesLoadComplete" )]
	//[Event( type="asf.events.ApplicationLoadProgressEvent", name="dependenciesLoadError" )]
	
	public class BaseLoadable extends EventDispatcher
	{
		private const STYLE_KEY_PREFIX:String = "$_style_";
		private var dependenciesLoader:AppLoader;
		
		private var _isLoaded:Boolean = false;
		private var _isLoading:Boolean = false;
		
		private var dict:LanguageDictionary;
		protected var rawXML:XML;
		private var _forms:Forms;
		private var _styles:StyleSheet;
		private var app:Application;
		
		private var extraDependencies:Object;
		
		private var loadingPaused:Boolean = false;
		
		public function BaseLoadable( p_app:Application )
		{
			super( this );
			app = p_app;
			extraDependencies = new Object( );
		}
		
		public function get isLoading( ):Boolean
		{
			return _isLoading;
		}
		
		public function get isLoaded( ):Boolean
		{
			return _isLoaded || ( this.dependencies != null && this.dependencies.itemsTotal == 0 );
		}
		
		public function addExtraDependency( id:String, url:String, type:String, props:Object = null ):void
		{
			extraDependencies[ id ] = new XML( this.formatDependencyNode( id, url, type, props ) ); 
		}
		
		public function removeExtraDependency( id:String ):void
		{
			delete extraDependencies[ id ];
		}
		
		public function clearExtraDependencies( ):void
		{
			extraDependencies = new Object( );
		}
		
		protected function initDependencies( p_dependenciesNode:XMLList, useOwnContext:Boolean = false ):void
		{
			var formItems:XMLList = this.rawXML.forms.form;
			var styleItems:XMLList = this.rawXML.style;
			
			var formItem:XML;
			var styleItem:XML;
			
			var formID:String;
			var formPath:String;
			var extraKey:String;
			
			var dependenciesNode:XMLList = p_dependenciesNode.copy( );
			
			var n:uint = 0;
			var sID:String;
			
			for each( styleItem in styleItems )
			{
				if( String( styleItem.src ) != "" || String( styleItem.@src ) != "" )
				{
					sID = styleItem.@id || styleItem.id;
					
					if( String( sID ) == "" )
					{
						sID = "$_style_" + n;
						n++;
					}
					
					dependenciesNode.file += new XML( formatDependencyNode( sID , styleItem.src || styleItem.@src, BulkLoader.TYPE_TEXT ) );
				}
			}
			
			for each( formItem in formItems )
			{
				formID = formItem.@id || formItem.id;
				formPath = formItem.@src || formItem.src;
				
				dependenciesNode.file += new XML( formatDependencyNode( formID, formPath, "xml" ) );
			}
			
			for( extraKey in extraDependencies )
			{
				dependenciesNode.file += extraDependencies[ extraKey ];
			}
			
			dependenciesLoader = new AppLoader( dependenciesNode, app, useOwnContext );
			
			dependenciesLoader.addEventListener( BulkProgressEvent.COMPLETE, dependenciesOnComplete );
			dependenciesLoader.addEventListener( BulkProgressEvent.PROGRESS, dependenciesOnProgress );
			dependenciesLoader.addEventListener( Event.ACTIVATE, dependenciesOnActivate );
			dependenciesLoader.addEventListener( Event.DEACTIVATE, dependenciesOnDeactivate );
			
			_isLoaded = false;
			_isLoading = true;
			
			dependenciesLoader.start( );
			
			this.dispatchEvent( new ApplicationLoadProgressEvent( ApplicationLoadProgressEvent.DEPENDENCIES_LOAD_START ) );
		}
		
		public function pauseLoading( ):void
		{
			loadingPaused = true;
			_isLoading = false;
			
			if( dependenciesLoader != null )
			{
				dependenciesLoader.pause( );
			}
		}
		
		public function resumeLoading( ):void
		{
			loadingPaused = false;
			_isLoading = true;
			
			if( dependenciesLoader != null )
			{
				dependenciesLoader.resume( );
			}
		}
		
		protected function formatDependencyNode( id:String, url:String, type:String, extraProps:Object = null ):String
		{
			var propsString:String = "";
			var k:String;
			
			if( extraProps )
			{	
				for( k in extraProps )
				{
					propsString += k + "='" + extraProps[ k ] + "' ";
				}
			}
			
			return "<file type='" + type + "' id='" + id + "' url='" + url + "' " + propsString + "/>";
		}
		
		public function get forms( ):Forms
		{
			if( this.rawXML != null && this._forms == null && isLoaded )
			{
				_forms = new Forms( XML( rawXML.forms ), app, dependencies );
			}
			
			return _forms;
		}
		
		public function get styles( ):StyleSheet
		{
			if( _styles == null )
			{
				parseStyles( );
			}
			
			return _styles;
		}
		
		public function getClassStyles( instance:* ):Object
		{
			var name:String = getQualifiedClassName( instance );
			name = name.split( "::" ).join( "." ).split( "." ).pop( );
			
			return styles.getStyle( name );
		}
		
		public function getStyleByID( id:String ):StyleSheet
		{
			var rawNode:XML = XML( this.rawXML.style.( @id == id ) );
			var result:StyleSheet = new StyleSheet( );
			var css:String = "";
			
			css += String( rawNode.text( ) );
			
			if( this.dependencies.hasItem( id ) )
			{
				css += this.dependencies.getText( id );
			}
			
			result.parseCSS( css );
			
			return result;
		}
		
		private function parseStyles( ):StyleSheet
		{
			var styleDoc:String;
			var allStyles:String = "";
			var n:uint = 0;
			var k:String = STYLE_KEY_PREFIX;
			var stylesXML:XMLList = this.rawXML.style;
			var styleXML:XML;
			
			for each( styleXML in stylesXML )
			{
				allStyles += styleXML.text( ).toString( );
				
				if( String( styleXML.@id || styleXML.id ) != "" && this.dependencies != null && this.dependencies.hasItem( styleXML.@id || styleXML.id ) )
				{
					allStyles += this.dependencies.getText( styleXML.@id || styleXML.id );
				}
			}
			
			while( this.dependencies != null && this.dependencies.hasItem( k + n ) )
			{
				styleDoc = this.dependencies.getText( k + n );
				allStyles += styleDoc;
				
				n++;
			}
			
			_styles = new StyleSheet( );
			_styles.parseCSS( allStyles );
			
			return _styles;
		}
		
		public function stopLoading( ):void
		{
			dependenciesLoader.pause( );
			
			_isLoading = false;
		}
		
		protected function dependenciesOnComplete( evt:BulkProgressEvent ):void
		{
			_isLoaded = true;
			_isLoading = false;
			
			parseStyles( );
			
			this.dispatchEvent( new ApplicationLoadProgressEvent( ApplicationLoadProgressEvent.DEPENDENCIES_LOAD_COMPLETE ) );
		}
		
		protected function dependenciesOnProgress( evt:BulkProgressEvent ):void
		{
			this.dispatchEvent( evt );
		}
		
		private function dependenciesOnActivate( evt:Event ):void
		{
			
		}
		
		private function dependenciesOnDeactivate( evt:Event ):void
		{
			
		}
		
		public function get dependencies( ):BulkLoader
		{
			if( dependenciesLoader == null ) return null;
			return dependenciesLoader.getBulkLoader( );
		}
		
		public function get dictionary( ):LanguageDictionary
		{
			trace( "[HI Framework] SUGGEST: Use '_' property instead of 'dictionary' to compact your code" );
			return getDictionary( );
		}
		
		public function get _( ):LanguageDictionary
		{
			return getDictionary( );
		}
		
		private function getDictionary( ):LanguageDictionary
		{
			if( dict == null )
			{
				var xml:XML;
				var data:Array = new Array( );
				var n:uint = 0;
				
				xml = dependencies.getXML( "$_dict_0" );
				
				if( xml != null )
				{
					while( xml != null && xml.children( ).length( ) > 0 )
					{
						xml = dependencies.getXML( "$_dict_" + n );
						data.push( xml );
						
						n++;
					}
				}
				
				dict = new LanguageDictionary( data );
			}
			
			return dict;
		}
		
		public function unload( ):void
		{
			_isLoaded = false;
			_isLoading = false;
			
			if( dependenciesLoader )
			{
				dependenciesLoader.dispose( );
				
				dependenciesLoader.removeEventListener( BulkProgressEvent.COMPLETE, dependenciesOnComplete );
				dependenciesLoader.removeEventListener( BulkProgressEvent.PROGRESS, dependenciesOnProgress );
				dependenciesLoader.removeEventListener( Event.ACTIVATE, dependenciesOnActivate );
				dependenciesLoader.removeEventListener( Event.DEACTIVATE, dependenciesOnDeactivate );
				
				dependenciesLoader = null;
			}
		}
		
		public function dispose( unloadDependencies:Boolean = true ):void
		{
			if( unloadDependencies ) unload( );
			
			if( _forms ) _forms.dispose( );
			_forms = null;
		}
	}
}